package newcrunch.codeanalysis;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.*;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.xml.parsers.ParserConfigurationException;
import jsyntaxpane.components.Markers;
import newcrunch.gui.RightTab;
import newcrunch.util.DisplayHelper;
import newcrunch.util.Logging;
import newcrunch.util.Misc;
import newcrunch.util.PropertyLoader;
import org.xml.sax.SAXException;

/**
 *
 * @author ccadm
 */
public class CodeAnalysisManager 
{
    private final CodeAnalysis display;
    private RightTab rightTab;
    
    private static Color highlightColor = Color.ORANGE;
    private Markers.SimpleMarker marker;
    
    static
    {
        highlightColor = PropertyLoader.getColorProperty("codeTokenColor");
    }
    
    public CodeAnalysisManager(JFrame frame)
    {
        marker = new Markers.SimpleMarker(highlightColor);
        
        display = new CodeAnalysis(frame, false);
        
        display.addWindowListener(new WindowAdapter(){
            @Override
           public void windowClosed(WindowEvent e) {
               Markers.removeMarkers(rightTab.getEditor(), marker);
           }
        });
        
        display.getTestButton().addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent evt) {
                runAnalysis();
            }
        });
        
        display.getDisplayTree().addMouseListener(new MouseAdapter(){
            @Override
            public void mouseClicked(MouseEvent evt) {
                InfoNode target = (InfoNode)display.getDisplayTree().getLastSelectedPathComponent();
                
                if (target != null && evt.getButton() == MouseEvent.BUTTON1)
                {
                    int startPos = target.getStartPos();
                    int endPos = target.getEndPos();
                    
                    if (startPos != -1)
                    {
                        JEditorPane editor = rightTab.getEditor();
                        Markers.removeMarkers(editor, marker);
                        editor.setCaretPosition(startPos);
                        DisplayHelper.centerLineInScrollPane(editor);
                        
                        if (endPos != -1)
                            Markers.markText(editor, startPos, endPos, marker);
                    }
                }
            }
        });
    }
    
    public void setRightTab(RightTab tab)
    {
        rightTab = tab;
    }
    
    public void runAnalysis()
    {
        final InfoNode rootNode = new InfoNode("");
        
        final ProgressDisplay dialog = new ProgressDisplay(Misc.getBaseGUI(rightTab), false);
        
        //displays the progress dialog
        if (dialog.isDisplayable() && !dialog.allDone())
        {
            dialog.pack();
            dialog.setVisible(true);
        }
        
        final SwingWorker<InfoNode, Void> csWorker = execCheckstyle(dialog);
        final SwingWorker<InfoNode, Void> pmdWorker = execPMD(dialog);
        final SwingWorker<InfoNode, Void> fbWorker = execFindbugs(dialog);
        
        new SwingWorker<Void, Void>(){
            @Override
            protected Void doInBackground() {
                addNode(rootNode, csWorker);
                addNode(rootNode, pmdWorker);
                addNode(rootNode, fbWorker);
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        display.getDisplayTree().setModel(new DefaultTreeModel(rootNode));

                        updateCounts(display.getDisplayTree().getModel());

                        display.getDisplayTree().setShowsRootHandles(true);
                        display.getDisplayTree().revalidate();
                            
                        EventQueue.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                display.setVisible(true); 
                                display.toFront();
                            }
                        });
                    }
                });
                return null;
            }
            
        }.execute();
    }
    
    /**
     * Retrieves the result from the worker and adds it to the rootNode.
     * @param rootNode
     * @param worker 
     */
    private void addNode(InfoNode rootNode, SwingWorker<InfoNode, Void> worker)
    {
        try {
            InfoNode ret = worker.get();
            
            if (ret != null)
                rootNode.add(ret);
        } catch (InterruptedException ex) {
            Logging.log(ex);
        } catch (ExecutionException ex) {
            Logging.log(ex);
        } 
    }
    
    private SwingWorker<InfoNode, Void> execCheckstyle(final ProgressDisplay dialog)
    {
        SwingWorker<InfoNode, Void> csWorker = new SwingWorker<InfoNode, Void>() {
            @Override
            protected InfoNode doInBackground() {
                return CheckStylePlugin.runCheckStyle(rightTab);
            }
            
            @Override
            protected void done()
            {
                dialog.setCheckstyleDone();
            }
        };
        csWorker.execute();
        return csWorker;
    }
    
    private SwingWorker<InfoNode, Void> execPMD(final ProgressDisplay dialog)
    {
        final ProgressChangeListener listener = new ProgressChangeListener(dialog.getPMDStatusDisp(), dialog.getPMDProgressBar());
        SwingWorker<InfoNode, Void> pmdWorker = new SwingWorker<InfoNode, Void>() {
            @Override
            protected InfoNode doInBackground() {
                try {
                    return PMDPlugin.runPMD(rightTab, listener);
                }
                catch (IOException ex) {
                    Logging.log(ex);
                    return null;
                }
                catch (ParserConfigurationException ex) {
                    Logging.log(ex);
                    return null;
                }
                catch (SAXException ex) {
                    Logging.log(ex);
                    return null;
                }
            }
            
            @Override
            protected void done()
            {
                dialog.setPMDDone();
            }
        };
        pmdWorker.addPropertyChangeListener(
                    new ProgressChangeListener(
                        dialog.getFindbugsStatusDisp(), 
                        dialog.getFindbugsProgressBar()));
        pmdWorker.execute();     
        return pmdWorker;
    }
    
    private SwingWorker<InfoNode, Void> execFindbugs(final ProgressDisplay dialog)
    {
        SwingWorker<InfoNode, Void> fbWorker = new SwingWorker<InfoNode, Void>() {
            @Override
            protected InfoNode doInBackground() {
                try {
                    return FindBugsPlugin.runFindBugs(rightTab, this);
                } catch (Exception ex) {
                    Logger.getLogger(CodeAnalysisManager.class.getName()).log(Level.SEVERE, null, ex);
                    return null;
                }
            }
            
            @Override
            protected void done()
            {
                dialog.setFindbugsDone();
            }
        };
        fbWorker.addPropertyChangeListener(
                    new ProgressChangeListener(
                        dialog.getFindbugsStatusDisp(), 
                        dialog.getFindbugsProgressBar()));
        fbWorker.execute();
        return fbWorker;
    }
    
    /**
     * Appends a number to a non-leaf node to indicate the number of 
     * children it has.
     * @param model 
     */
    private void updateCounts(TreeModel model)
    {
        TreeNode root = (TreeNode)model.getRoot();
        int total = root.getChildCount();
        for (int i = 0; i < total; i++)
        {
            InfoNode curr = (InfoNode)root.getChildAt(i);
            int currTotal = curr.getChildCount();
            curr.setUserObject(curr.getUserObject() + " (" + currTotal + ")");
            
            if (curr.getUserObject().toString().startsWith("PMD"))
            {
                for (int j = 0; j < currTotal; j++)
                {
                    InfoNode inner = (InfoNode)curr.getChildAt(j);
                    inner.setUserObject(inner.getUserObject() + " (" + (inner.getChildCount() - 1) + ")");
                }
            }
            else
            {
                for (int j = 0; j < currTotal; j++)
                {
                    InfoNode inner = (InfoNode)curr.getChildAt(j);
                    inner.setUserObject(inner.getUserObject() + " (" + inner.getChildCount() + ")");
                }
            }
        }
    }
    

}
